using System;
using System.Collections.Generic;
using System.Text;
using CodePlex.SpaceBlock.FS;
using CodePlex.Resourceful;
using CodePlex.Resourceful.Amazon.S3;

namespace CodePlex.SpaceBlock.Tasks
{
    abstract public class GenericS3Base : GenericTask
    {

        public class Properties
        {
            public static readonly string AccessKeyID = "AccessKeyID";
            public static readonly string SecretAccessKey = "SecretAccessKey";
            public static readonly string DestinationPath = "DestinationPath";
        }


        protected void MonitorTransfer(long bytes)
        {
            //LogMessage("Transferred {0}", Utils.ComputeSizeString(bytes));
        }

        abstract protected void InternalExecute(S3FileSystem fileSystem);


        protected string GetTrimmed(string name, bool required)
        {

            string value = this.GetProperty(name);

            if (value != null)
            {
                value = value.Trim();
            }

            if (required && string.IsNullOrEmpty(value))
            {
                throw CodePlexException.Format("Missing required parameter '{0}'.", name);
            }

            return value;
        }

        public override bool Execute()
        {
            try
            {

                string accessKeyID = this.GetTrimmed(Properties.AccessKeyID,true);
                string secretAccessKey = this.GetTrimmed(Properties.SecretAccessKey, true);

                S3Connection connection = new S3Connection( accessKeyID,secretAccessKey,true,RequestStyle.Path);

                S3FileSystem fileSystem = new S3FileSystem(connection);


                this.InternalExecute(fileSystem);

                return true;

            }
            catch (Exception ex)
            {
                this.LogError(ex.Message);
                return false;
            }
        }














        private string DisplayPath(IFileSystem fs, Path path)
        {
            LocalFileSystem lfs = fs as LocalFileSystem;
            return lfs == null ? path.ToString() : lfs.ToLocalPath(path);
        }


        protected void TransferFile(IFileSystem sourceFS, IFileSystem destFS, Path source, Path dest, NameValuePairs metadata)
        {

            this.LogInfo("Transferring '{0}' to '{1}'...", DisplayPath(sourceFS, source), DisplayPath(destFS,dest));


            destFS.EnsureFolderExists(dest.Parent);


            IFileItem item = sourceFS.GetItem(source) as IFileItem;

            ISupportsTransferWithMetadata m = destFS as ISupportsTransferWithMetadata;
            if (m != null)
            {
                m.Transfer(item, dest, metadata, this.MonitorTransfer);
            }
            else
            {
                destFS.Transfer(item, dest, this.MonitorTransfer);
            }
        


        }

        protected void TransferFolder(IFileSystem sourceFS, IFileSystem destFS, Path source, Path dest, NameValuePairs metadata)
        {
            Path destFolder = dest.Combine(source.Name + Path.SeparatorChar);

            destFS.EnsureFolderExists(destFolder);


            foreach (IFolderItem sourceChildFolder in sourceFS.GetFolders(source))
            {
                this.TransferFolder(sourceFS, destFS, sourceChildFolder.Path, destFolder,metadata);
            }

            foreach (IFileItem sourceChildFile in sourceFS.GetFiles(source))
            {
                Path destFile = destFolder.Combine(sourceChildFile.Path.Name);
                this.TransferFile(sourceFS, destFS, sourceChildFile.Path, destFile,metadata);
            }

        }

        protected void InternalTransfer(IFileSystem sourceFS, IFileSystem destFS, Path sourcePath, Path destPath, NameValuePairs metadata)
        {

            if (!sourceFS.Exists(sourcePath))
            {
                throw CodePlexException.Format("SourcePath '{0}' not found.", sourcePath);
            }


            // folder -> folder:  recursive
            if (sourcePath.IsFolder && destPath.IsFolder)
            {
                this.TransferFolder(sourceFS, destFS, sourcePath, destPath,metadata);



            }
            else
            {

                // folder -> file: unsupported
                if (sourcePath.IsFolder && !destPath.IsFolder)
                {
                    throw CodePlexException.Format("Cannot transfer a folder ({0}) to a file ({1}).", sourcePath, destPath);
                }


                // file -> folder: compute dest file
                if (!sourcePath.IsFolder && destPath.IsFolder)
                {
                    destPath = destPath.Combine(sourcePath.Name);
                }

                // file -> file
                this.TransferFile(sourceFS, destFS, sourcePath, destPath,metadata);
            }



        }

    }
}
